-
Notifications
You must be signed in to change notification settings - Fork 13
Support prometheus metrics #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Hi @7ing. Thanks for your PR. I'm waiting for a cert-manager member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
/ok-to-test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, thanks Jing 🙌 the integration and unit tests here make it far easier to review confidently!
My main questions/concerns are around the construction logic in the metrics
subpackage, which I think we need to decouple from net.Listener
(and allow more flexibility for projects that already have their own prometheus.Registry
they'd like to re-use).
metrics/metrics.go
Outdated
} | ||
|
||
// NewServer registers Prometheus metrics and returns a new Prometheus metrics HTTP server. | ||
func (m *Metrics) NewServer(ln net.Listener) *http.Server { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't called outside of test cases, and I guess that is by design as it is expected that the corresponding implementation should call NewServer
on metrics.Metrics
to register their Listener.
Could you possibly update the example/
implementation in the root of this repository to demonstrate how to actually add the /metrics
endpoint? I also wonder if the Managed should be extended to be able to auto-serve this endpoint in cases where a user doesn't need to provide their own listener (but does want metrics to be served).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we renamed this to Register(*prometheus.Registry)
rather than tying the net.Listener
logic into the registration?
We can always have/find some kind of csihelpers.Handle(*http.Server, *prometheus.Registry)
function elsewhere then, which needn't be opinionated about csi-lib.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you possibly update the example/ implementation in the root of this repository to demonstrate how to actually add the /metrics endpoint?
Yes, it is there. And modified accordingly based on recent changes.
https://github.com/7ing/csi-lib/blob/abf15631238fa809d10d9c206178c414d9495b4f/test/integration/metrics_test.go#L83-L95
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestions. I have changed to use a DefaultHandler
instead, which could be served as a reference implementation for http handler.
metrics/metrics.go
Outdated
) | ||
|
||
// Create Registry and register the recommended collectors | ||
registry := prometheus.NewRegistry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could there be cases where a user wants to provide their own Registry and have these metrics registered into it, so they can be served alongside driver-specific metrics? This would make the library more composable with existing drivers that may serve their own metrics already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, code changed per your suggestion.
metrics/metrics.go
Outdated
registry := prometheus.NewRegistry() | ||
registry.MustRegister( | ||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), | ||
collectors.NewGoCollector(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like these should be auto-registered, but I think the Metrics
struct itself should be able to be setup so that it doesn't embed these Go metrics into them (i.e. so it only adds in the csi-lib specific metrics to the registry).
Perhaps we need some 'basic' constructor function for the csi-lib metrics, with some form of NewRecommendedMetrics
function that calls the basic one, but also adds in standard Go-esque metrics? Not sure :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, I will remove those default registry and let the caller to pass their Registry instead.
@munnerz Thank you for your valuable inputs. Sorry took so long to make the change. But I guess this version addressed most of your concerns. |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
||
// Should expose that CertificateRequest as ready with expiry and renewal time | ||
// node="f56fd9f8b" is the hash value of "test-node" defined in driver_testing.go | ||
expectedOutputTemplate := `# HELP certmanager_csi_certificate_request_expiration_timestamp_seconds The date after which the certificate request expires. Expressed as a Unix Epoch Time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will certmanager_csi_...
be the prefix for the metrics when exposed in https://github.com/cert-manager/csi-driver?
Can we make sure they are all using similar names? Does this happen automatically?
Could you provide an example of before and after for https://github.com/cert-manager/csi-driver with these changes applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @inteon for your review.
Yes, certmanager_csi_...
will be the prefix for the metrics when in the csi-driver. It is part of the definition from:
https://github.com/7ing/csi-lib/blob/b9186bad5b6f9af9bc93dbd28571d2d9219700e6/metrics/metrics.go#L27-L31
We choose this name based on cert-manager.io definition: https://github.com/cert-manager/cert-manager/blob/5e09ef6c0552df0bde64746c735cb1ff324b6261/pkg/metrics/metrics.go#L44
All cert-manager controllers have certmanager_..
prefix.
Our https://github.com/cert-manager/csi-driver does not have any certmanager related metrics. Currently it only serves k8s components metrics, like cpu / mem etc. That's why this PR exist. This test files show the expected output regarding certmanager metrics (besides the k8s metrics upon driver configuration).
@7ing We have made some major dependency upgrades in this module. Are you able to rebase your PR, preparing for another round of review? Sorry for the inconvenience and for the delays in review. 😒 |
Following metrics added: certmanager_csi_certificate_request_expiration_timestamp_seconds certmanager_csi_certificate_request_ready_status certmanager_csi_certificate_request_renewal_timestamp_seconds certmanager_csi_driver_issue_call_count certmanager_csi_driver_issue_error_count certmanager_csi_managed_certificate_count certmanager_csi_managed_volume_count fixes: cert-manager#60 Signed-off-by: Jing Liu <[email protected]>
fixes: cert-manager#60 Signed-off-by: Jing Liu <[email protected]>
Signed-off-by: Jing Liu <[email protected]>
Signed-off-by: Jing Liu <[email protected]>
/retest |
@erikgb done with rebase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great stuff! Thanks @7ing! I did my first pass on this PR now, and I think I would prefer using a Prometheus collector to avoid the add/remove metrics for metrics based on API resources. Please take a look and let me know what you think! It's not a blocker, but a pattern we are adopting in cert-manager projects nowadays.
if opts.Metrics == nil { | ||
opts.Metrics = metrics.New(opts.Log, prometheus.NewRegistry()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should avoid creating a new Prometheus registry if metrics are not enabled. If I am not mistaken, this will only waste CPU and memory. I would suggest removing the DefaultHandler
function, making the user of csi-lib responsible for managing the handler on top of the registry supplied to csi-lib. This will probably require some changes to the code producing metrics. Perhaps an interface (with a no-op implementation) could be used to avoid cluttering the functional code?
} | ||
|
||
actualDuration := crt.NotAfter.Sub(crt.NotBefore) | ||
|
||
renewBeforeNotAfter := actualDuration / 3 | ||
|
||
return crt.NotAfter.Add(-renewBeforeNotAfter), nil | ||
return crt.NotAfter, crt.NotAfter.Add(-renewBeforeNotAfter), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return crt.NotAfter, crt.NotAfter.Add(-renewBeforeNotAfter), nil | |
return crt.NotAfter, crt.NotAfter.Sub(renewBeforeNotAfter), nil |
Nit: I find this a bit easier to read.
// getExpiryAndDefaultNextIssuanceTime will return the certificate expiry time, together with | ||
// default time at which the certificate should be renewed by the driver- 2/3rds through its | ||
// lifetime (NotAfter - NotBefore). | ||
func getExpiryAndDefaultNextIssuanceTime(chain []byte) (time.Time, time.Time, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a huge fan of function names containing the And
term. Isn't the expiry timestamp the most important return param now? Could we just name it something like calculateExpiryTime? Open for better suggestions, but preferably without the "and". 😆
m.certificateRequestExpiryTimeSeconds.DeletePartialMatch(prometheus.Labels{"name": name, "namespace": namespace}) | ||
m.certificateRequestRenewalTimeSeconds.DeletePartialMatch(prometheus.Labels{"name": name, "namespace": namespace}) | ||
m.certificateRequestReadyStatus.DeletePartialMatch(prometheus.Labels{"name": name, "namespace": namespace}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use a Prometheus Collector for metrics derived from API resources. This will prevent bugs like the one fixed by cert-manager/cert-manager#7856. API resources should be cached in all controllers, so just querying the API, which should hit cache, is not a problem.
certmanager_csi_certificate_request_expiration_timestamp_seconds certmanager_csi_certificate_request_ready_status
certmanager_csi_certificate_request_renewal_timestamp_seconds certmanager_csi_driver_issue_call_count_total
certmanager_csi_driver_issue_error_count_total
certmanager_csi_managed_certificate_count_total
certmanager_csi_managed_volume_count_total
fixes: #60